Introduction to TutorialD via Jupyter Notebook

Thanks for trying Project:M36 via this Jupyter notebook. TutorialD is a language for interacting with relational algebra databases, similar to SQL but without the historical cruft. This notebook is based on the TutorialD Tutorial.

Project:M36 is a feature-rich database management system (DBMS) which casts off the baggage of legacy DBMSes by adhering strictly to the mathematics of the relational algebra.

Please refer to the Project:M36 documentation for more detailed examples. Let's get started!

First, let's import the Chris Date example relation variables. The relation variables provided in the example are:

  • s suppliers
  • sp supplier-parts
  • p parts

In [1]:
:importexample date


ok

If you see an error in the previous cell regarding project-m36-websocket-server not being in your PATH, add the binary to your PATH and restart the jupyter server.

Let's take a look at the relation variables.


In [2]:
:showexpr s


s#::Textsname::Textstatus::Integercity::Text
"S3""Blake"30"Paris"
"S5""Adams"30"Athens"
"S4""Clark"20"London"
"S1""Smith"20"London"
"S2""Jones"10"Paris"
Finite 5 tuples

In [3]:
:showexpr sp


s#::Textp#::Textqty::Integer
"S1""P2"200
"S1""P1"300
"S3""P2"200
"S2""P2"400
"S4""P5"400
"S1""P5"100
"S1""P6"100
"S2""P1"300
"S1""P4"200
"S4""P4"300
"S4""P2"200
"S1""P3"400
Finite 12 tuples

In [4]:
:showexpr p


p#::Textpname::Textcolor::Textweight::Integercity::Text
"P3""Screw""Blue"17"Oslo"
"P2""Bolt""Green"17"Paris"
"P4""Screw""Red"14"London"
"P6""Cog""Red"19"London"
"P1""Nut""Red"12"London"
"P5""Cam""Blue"12"Paris"
Finite 6 tuples

Relational Operators

Restriction

We can see that there is a many-to-many relationship between s (suppliers) and p (parts) via the sp relation variable.

Let's look at the suppliers who are in London. This is called "restriction".

Note that the relation attributes have appropriate types which restrict the values which they can contain.


In [5]:
:showexpr s where city="London"


s#::Textsname::Textstatus::Integercity::Text
"S4""Clark"20"London"
"S1""Smith"20"London"
Finite 2 tuples

Join And Projection

Let's examine which parts the London suppliers offer. In the following expression, we make use of the relational join operator (equivalent to NATURAL JOIN in SQL) and projection using the trailing curly brackets to only return the relation attributes which interest us.


In [6]:
:showexpr ((s where city="London") join sp join p){p#,pname,qty}


qty::Integerpname::Textp#::Text
100"Cog""P6"
300"Nut""P1"
300"Screw""P4"
200"Screw""P4"
Finite 4 tuples

Extend and Atom Functions

We can also execute server-side functions on values and add them to our result relation.


In [7]:
:showexpr (s:{statusplus10:=add(@status,10)}){s#,status,statusplus10}


s#::Textstatus::Integerstatusplus10::Integer
"S1"2030
"S4"2030
"S5"3040
"S3"3040
"S2"1020
Finite 5 tuples

Group and Ungroup

Unlike most databases, Project:M36 supports relation-valued attributes. This means that database values (atoms) can also be relations. Relation-valued attributes can be represented in the notebook as nested tables of values. Note that the nested relation's type is relation {qty::Integer,pname::Text,s#::Text}.

Relation-valued attributes reduce tuple duplication by clearly associating certain values with their related multiple tuples. The group operator can also be used to create the SQL equivalent of OUTER JOIN but without the need for representing NULL in any form.

Let's list the available counts for each supplier for each part.


In [8]:
:showexpr (p join sp){qty,pname,s#,p#} group ({s#,pname,qty} as availability)


p#::Textavailability::relation {qty::Integer,pname::Text,s#::Text}
"P6"
qty::Integerpname::Texts#::Text
100"Cog""S1"
Finite 1 tuples
"P1"
qty::Integerpname::Texts#::Text
300"Nut""S2"
300"Nut""S1"
Finite 2 tuples
"P2"
qty::Integerpname::Texts#::Text
200"Bolt""S4"
200"Bolt""S1"
400"Bolt""S2"
200"Bolt""S3"
Finite 4 tuples
"P4"
qty::Integerpname::Texts#::Text
200"Screw""S1"
300"Screw""S4"
Finite 2 tuples
"P3"
qty::Integerpname::Texts#::Text
400"Screw""S1"
Finite 1 tuples
"P5"
qty::Integerpname::Texts#::Text
400"Cam""S4"
100"Cam""S1"
Finite 2 tuples
Finite 6 tuples

We can flatten the relation-valued attributes, thereby generating more top-level tuples.


In [9]:
:showexpr ((p join sp){qty,pname,s#,p#} group ({s#,pname,qty} as availability)) ungroup availability


qty::Integerpname::Texts#::Textp#::Text
400"Screw""S1""P3"
200"Bolt""S4""P2"
100"Cog""S1""P6"
100"Cam""S1""P5"
300"Nut""S2""P1"
400"Cam""S4""P5"
300"Screw""S4""P4"
200"Bolt""S1""P2"
200"Screw""S1""P4"
200"Bolt""S3""P2"
300"Nut""S1""P1"
400"Bolt""S2""P2"
Finite 12 tuples

Database State Operators

Thus far, we have only been inspecting the database state. Next, let's cover some database update operators. These operators are all similar to SQL operators, so we don't need a lot of exposition.

Insert


In [10]:
insert s relation{tuple{s# "S6", sname "Bob", status 50, city "Boston"}}


ok

In [11]:
:showexpr s


city::Texts#::Textsname::Textstatus::Integer
"Boston""S6""Bob"50
"Paris""S3""Blake"30
"Athens""S5""Adams"30
"London""S4""Clark"20
"London""S1""Smith"20
"Paris""S2""Jones"10
Finite 6 tuples

Delete


In [12]:
delete s where sname="Bob"


ok

In [13]:
:showexpr s


city::Texts#::Textsname::Textstatus::Integer
"Paris""S3""Blake"30
"Athens""S5""Adams"30
"London""S4""Clark"20
"London""S1""Smith"20
"Paris""S2""Jones"10
Finite 5 tuples

Update


In [14]:
update s where city="London" (status:=90)


ok

In [15]:
:showexpr s


city::Texts#::Textsname::Textstatus::Integer
"London""S1""Smith"90
"Paris""S3""Blake"30
"Athens""S5""Adams"30
"London""S4""Clark"90
"Paris""S2""Jones"10
Finite 5 tuples

Assign


In [16]:
london_suppliers := s where city="London"


ok

In [17]:
:showexpr london_suppliers


city::Texts#::Textsname::Textstatus::Integer
"London""S1""Smith"90
"London""S4""Clark"90
Finite 2 tuples

Constraints

Project:M36 supports too many features to cover in one notebook, but constraints are certainly essential.

Let's examine the current constraints.


In [18]:
:constraints


name::Textsub::Textsuper::Text
"p_pkey""NotEquals (Project (AttributeNames (fromList [\"b\"])) (Extend (AttributeExtendTupleExpr \"b\" (FunctionAtomExpr \"count\" [AttributeAtomExpr \"a\"] ())) (Extend (AttributeExtendTupleExpr \"a\" (RelationAtomExpr (RelationVariable \"p\" ()))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]})))))) (Project (AttributeNames (fromList [\"b\"])) (Extend (AttributeExtendTupleExpr \"b\" (FunctionAtomExpr \"count\" [AttributeAtomExpr \"a\"] ())) (Extend (AttributeExtendTupleExpr \"a\" (RelationAtomExpr (Project (AttributeNames (fromList [\"p#\"])) (RelationVariable \"p\" ())))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]}))))))""ExistingRelation (Relation [] (RelationTupleSet {asList = []}))"
"s_pkey""NotEquals (Project (AttributeNames (fromList [\"b\"])) (Extend (AttributeExtendTupleExpr \"b\" (FunctionAtomExpr \"count\" [AttributeAtomExpr \"a\"] ())) (Extend (AttributeExtendTupleExpr \"a\" (RelationAtomExpr (RelationVariable \"s\" ()))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]})))))) (Project (AttributeNames (fromList [\"b\"])) (Extend (AttributeExtendTupleExpr \"b\" (FunctionAtomExpr \"count\" [AttributeAtomExpr \"a\"] ())) (Extend (AttributeExtendTupleExpr \"a\" (RelationAtomExpr (Project (AttributeNames (fromList [\"s#\"])) (RelationVariable \"s\" ())))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]}))))))""ExistingRelation (Relation [] (RelationTupleSet {asList = []}))"
"sp_pkey""NotEquals (Project (AttributeNames (fromList [\"b\"])) (Extend (AttributeExtendTupleExpr \"b\" (FunctionAtomExpr \"count\" [AttributeAtomExpr \"a\"] ())) (Extend (AttributeExtendTupleExpr \"a\" (RelationAtomExpr (RelationVariable \"sp\" ()))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]})))))) (Project (AttributeNames (fromList [\"b\"])) (Extend (AttributeExtendTupleExpr \"b\" (FunctionAtomExpr \"count\" [AttributeAtomExpr \"a\"] ())) (Extend (AttributeExtendTupleExpr \"a\" (RelationAtomExpr (Project (AttributeNames (fromList [\"p#\",\"s#\"])) (RelationVariable \"sp\" ())))) (ExistingRelation (Relation [] (RelationTupleSet {asList = [RelationTuple [] []]}))))))""ExistingRelation (Relation [] (RelationTupleSet {asList = []}))"
Finite 3 tuples

Constraints are represented as "inclusion dependencies". An inclusion dependency is a generalization of all possible constraints on the database and can represent keys, uniqueness constraints, foreign key constraints, as well as span multiple relation variables.

An inclusion dependency requires that the result of relational expression sub is a subset of relational expression super.

Project:M36 includes some utilities to generate inclusion dependencies.


In [19]:
sandwich := relation{tuple{name "Cheese", price 4},tuple{name "Tuna", price 5}}


ok

In [20]:
key sandwich_name{name} sandwich


ok

In [21]:
insert sandwich relation{tuple{name "Tuna", price 6}}


InclusionDependencyCheckError "sandwich_name"

Jupyter Kernel Features

By default, the TutorialD kernel will automatically start a project-m36-websocket-server when the first TutorialD command is executed. For this to work, project-m36-websocket-server must be in the jupyter server's PATH environment variable.

Magic

Like the python kernel, the TutorialD kernel includes some commands to interact directly with the kernel. These commands are not passed to the TutorialD interpreter.

  • %help brings up some help which includes links to additional documentation
  • %connect ws://<host>:<port> <dbname> connects to the project-m36-websocket-server already running. For example:
    %connect ws://localhost:64000 employees
    connects to the websocket server running on the localhost on port 64000 and selects the database named "employees". Note that the database name is not part of the websocket URL.

Conclusion

Project:M36 is a fully-featured relational algebra engine suitable for use a database. It sets out to prove that sticking to the mathematical underpinnings of the relational algebra is straightforward and results in improved correctness and performance.

If you would like to learn more, please refer to the documentation and join our welcoming community to ask questions.


In [ ]: